using System.IO;

namespace System
{
    public class StreamWrapper : Stream
    {
        public class SeekArgs : EventArgs
        {
            public long Offset { get; private set; }
            public SeekOrigin Origin { get; private set; }
            public long Result { get; private set; }

            public SeekArgs(long Offset, SeekOrigin Origin, long Result)
            {
                this.Offset = Offset;
                this.Origin = Origin;
                this.Result = Result;
            }
        }

        public class SetLengthArgs : EventArgs
        {
            public long Length { get; private set; }
            public SetLengthArgs(long Length) { this.Length = Length; }
        }

        public class ReadArgs : EventArgs
        {
            public byte[] Buffer { get; private set; }
            public int Offset { get; private set; }
            public int Count { get; private set; }
            public int Result { get; private set; }

            public ReadArgs(byte[] Buffer, int Offset, int Count, int Result)
            {
                this.Buffer = Buffer;
                this.Offset = Offset;
                this.Count = Count;
                this.Result = Result;
            }
        }

        public class WriteArgs : EventArgs
        {
            public byte[] Buffer { get; private set; }
            public int Offset { get; private set; }
            public int Count { get; private set; }

            public WriteArgs(byte[] Buffer, int Offset, int Count)
            {
                this.Buffer = Buffer;
                this.Offset = Offset;
                this.Count = Count;
            }
        }

        public class PositionChangedArgs : EventArgs
        {
            public long OldPosition { get; private set; }
            public long NewPosition { get; private set; }

            public PositionChangedArgs(long OldPosition, long NewPosition)
            {
                this.OldPosition = OldPosition;
                this.NewPosition = NewPosition;
            }
        }

        public event EventHandler OnFlush;
        public event EventHandler<SeekArgs> OnSeek;
        public event EventHandler<SetLengthArgs> OnSetLength;
        public event EventHandler<ReadArgs> OnRead;
        public event EventHandler<WriteArgs> OnWrite;
        public event EventHandler<PositionChangedArgs> OnPositionChanged;
        public event EventHandler OnEndOfStream;

        private readonly Stream f_DataStream;

        public StreamWrapper(Stream Source) { f_DataStream = Source; }


        public override void Flush()
        {
            f_DataStream.Flush();
            var handlers = OnFlush;
            if(handlers == null) return;
            handlers.Invoke(this, EventArgs.Empty);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            var result = f_DataStream.Seek(offset, origin);
            var handlers = OnSeek;
            if(handlers == null) return result;
            handlers.Invoke(this, new SeekArgs(offset, origin, result));
            return result;
        }

        public override void SetLength(long value)
        {
            f_DataStream.SetLength(value);
            var handlers = OnSetLength;
            if(handlers == null) return;
            handlers.Invoke(this, new SetLengthArgs(value));
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            var result = f_DataStream.Read(buffer, offset, count);
            var handlers = OnRead;
            if(handlers != null)
                handlers.Invoke(this, new ReadArgs(buffer, offset, count, result));
            if(EndOfStream)
            {
                var eof_handlers = OnEndOfStream;
                if(eof_handlers != null)
                    eof_handlers.Invoke(this, EventArgs.Empty);
            }
            return result;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            f_DataStream.Write(buffer, offset, count);
            var handlers = OnWrite;
            if(handlers == null) return;
            handlers.Invoke(this, new WriteArgs(buffer, offset, count));
        }

        public override bool CanRead { get { return f_DataStream.CanRead; } }

        public override bool CanSeek { get { return f_DataStream.CanSeek; } }

        public override bool CanWrite { get { return f_DataStream.CanWrite; } }

        public override long Length { get { return f_DataStream.Length; } }

        public override long Position
        {
            get { return f_DataStream.Position; }
            set
            {
                var old = f_DataStream.Position;
                f_DataStream.Position = value;
                var pos_handlers = OnPositionChanged;
                if(pos_handlers != null)
                    pos_handlers.Invoke(this, new PositionChangedArgs(old, value));
                if(!EndOfStream) return;
                var eof_handlers = OnEndOfStream;
                if(eof_handlers != null)
                    eof_handlers.Invoke(this, EventArgs.Empty);
            }
        }

        public bool EndOfStream { get { return f_DataStream.Position == f_DataStream.Length; } }

        public double Progress { get { return (double)f_DataStream.Position / f_DataStream.Length; } }
    }
}